/*
 * GPLv3 License
 *
 * Copyright (c) 2023-2025 4ra1n (Jar Analyzer Team)
 *
 * This project is distributed under the GPLv3 license.
 *
 * https://github.com/jar-analyzer/jar-analyzer/blob/master/LICENSE
 */

package me.n1ar4.jar.analyzer.gui.vul;

import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import me.n1ar4.jar.analyzer.engine.CoreHelper;
import me.n1ar4.jar.analyzer.engine.SearchCondition;
import me.n1ar4.jar.analyzer.gui.MainForm;
import me.n1ar4.jar.analyzer.utils.IOUtils;
import me.n1ar4.jar.analyzer.utils.YamlUtil;
import me.n1ar4.log.LogManager;
import me.n1ar4.log.Logger;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class VulnerabilityBuilder {
    private static final Logger logger = LogManager.getLogger();

    private static final int BTN_NUM_COLS = 3;

    private static JScrollPane javaVulSearchPanel;

    private static Rule rule = null;

    public static void refreshAll() {
        createUI(rule.getVulnerabilities());
    }

    public static void refreshLow() {
        if (rule == null || rule.getVulnerabilities() == null) {
            return;
        }
        Map<String, List<SearchCondition>> filtered = new LinkedHashMap<>();
        for (Map.Entry<String, List<SearchCondition>> entry : rule.getVulnerabilities().entrySet()) {
            if (entry.getValue() != null && !entry.getValue().isEmpty()) {
                String level = entry.getValue().get(0).getLevel();
                if ("low".equals(level)) {
                    filtered.put(entry.getKey(), entry.getValue());
                }
            }
        }
        createUI(filtered);
    }

    public static void refreshHigh() {
        if (rule == null || rule.getVulnerabilities() == null) {
            return;
        }
        Map<String, List<SearchCondition>> filtered = new LinkedHashMap<>();
        for (Map.Entry<String, List<SearchCondition>> entry : rule.getVulnerabilities().entrySet()) {
            if (entry.getValue() != null && !entry.getValue().isEmpty()) {
                String level = entry.getValue().get(0).getLevel();
                if ("high".equals(level)) {
                    filtered.put(entry.getKey(), entry.getValue());
                }
            }
        }
        createUI(filtered);
    }

    public static void refreshMedium() {
        if (rule == null || rule.getVulnerabilities() == null) {
            return;
        }
        Map<String, List<SearchCondition>> filtered = new LinkedHashMap<>();
        for (Map.Entry<String, List<SearchCondition>> entry : rule.getVulnerabilities().entrySet()) {
            if (entry.getValue() != null && !entry.getValue().isEmpty()) {
                String level = entry.getValue().get(0).getLevel();
                if ("medium".equals(level)) {
                    filtered.put(entry.getKey(), entry.getValue());
                }
            }
        }
        createUI(filtered);
    }

    private static void createUI(Map<String, List<SearchCondition>> rules) {
        if (javaVulSearchPanel.getViewport() != null &&
                javaVulSearchPanel.getViewport().getView() != null) {
            // 移除所有监听器以防止内存泄漏
            Component oldView = javaVulSearchPanel.getViewport().getView();
            if (oldView instanceof JPanel) {
                JPanel oldPanel = (JPanel) oldView;
                for (Component comp : oldPanel.getComponents()) {
                    if (comp instanceof JButton) {
                        for (ActionListener listener : ((JButton) comp).getActionListeners()) {
                            ((JButton) comp).removeActionListener(listener);
                        }
                    }
                }
            }
            // 清除视图
            javaVulSearchPanel.getViewport().setView(null);
        }

        // 重构 UI 代码允许自定义规则超过 27 个 2025/07/12
        JPanel corePanel = new JPanel();
        int row = rules.size() / BTN_NUM_COLS + 1;
        corePanel.setLayout(new GridLayoutManager(row, 3,
                new Insets(3, 3, 3, 3), -1, -1));

        Iterator<Map.Entry<String, List<SearchCondition>>> iter = rules.entrySet().iterator();
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < BTN_NUM_COLS; j++) {
                if (!iter.hasNext()) {
                    break;
                }
                Map.Entry<String, List<SearchCondition>> entry = iter.next();

                String vName = entry.getKey();
                JButton vButton = new JButton();
                vButton.setText(vName);

                if (entry.getValue() != null) {
                    if (!entry.getValue().isEmpty()) {
                        String level = entry.getValue().get(0).getLevel();
                        if (level != null) {
                            switch (level) {
                                case "high":
                                    vButton.setBackground(new Color(255, 204, 204));
                                    break;
                                case "medium":
                                    vButton.setBackground(new Color(204, 229, 255));
                                    break;
                                case "low":
                                    vButton.setBackground(new Color(242, 242, 242));
                                    break;
                            }
                            vButton.setOpaque(true);
                            vButton.setBorderPainted(true);
                        }
                    }
                }

                corePanel.add(vButton,
                        new GridConstraints(i, j, 1, 1,
                                GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
                                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                                GridConstraints.SIZEPOLICY_FIXED,
                                null,
                                null,
                                null,
                                0,
                                false));
                vButton.addActionListener(e -> {
                    if (MainForm.getEngine() == null || !MainForm.getEngine().isEnabled()) {
                        return;
                    }
                    new Thread(() -> CoreHelper.refreshCallSearchList(entry.getValue())).start();
                });
                // 2025/04/06
                // 这里没必要使用 INFO 级别 DEBUG 即可
                logger.debug("build {} vulnerability gui", entry.getKey());
            }
        }

        javaVulSearchPanel.setViewportView(corePanel);
        javaVulSearchPanel.revalidate(); // 重新验证布局
        javaVulSearchPanel.repaint();    // 重绘界面
    }

    private static void loadVulnerabilities() {
        Path vPath = Paths.get("vulnerability.yaml");
        InputStream is = null;
        if (!Files.exists(vPath)) {
            logger.warn("vulnerability.yaml not found");
            logger.info("use builtin vulnerability.yaml rule");
            is = VulnerabilityBuilder.class.getClassLoader().getResourceAsStream("vulnerability.yaml");
            if (is == null) {
                logger.error("builtin vulnerability.yaml not found");
                return;
            }
        } else {
            logger.info("use vulnerability.yaml rule");
            try {
                is = Files.newInputStream(vPath.toFile().toPath());
            } catch (Exception ignored) {
            }
        }
        try {
            byte[] yamlData = IOUtils.readAllBytes(is);
            rule = YamlUtil.loadAs(yamlData);
        } catch (Exception ex) {
            logger.error("load vulnerability.yaml failed : {}", ex.getMessage());
        }
    }

    public static void build(MainForm main) {
        javaVulSearchPanel = main.getJavaVulSearchPanel();
        loadVulnerabilities();
        createUI(rule.getVulnerabilities());
    }
}
